#include "CAnimals.h"
#include <stdio.h>

CAnimalsFactory AnimalsFactory;


//////////////////////////////////////////////////////////////////
//  CAnimals
//////////////////////////////////////////////////////////////////

CAnimals::CAnimals() : m_nCnt(0)
{
    AnimalsFactory.LockServer(TRUE);
}

CAnimals::~CAnimals()
{
    AnimalsFactory.LockServer(FALSE);
}

HRESULT __stdcall CAnimals::QueryInterface(REFIID riid, void** ppvObject)
{
    if (!ppvObject)
        return E_POINTER;

    *ppvObject = nullptr;

    if (IID_IDog == riid)
    {
        AddRef();
        *ppvObject = dynamic_cast<IDog*>(this);
    }
    else if (IID_ICat == riid)
    {
        AddRef();
        *ppvObject = dynamic_cast<ICat*>(this);
    }
    else if (IID_IPig == riid)
    {
        AddRef();
        *ppvObject = dynamic_cast<IPig*>(this);
    }
    else
    {
        return E_NOINTERFACE;
    }

    if (*ppvObject == nullptr)
        return S_FALSE;

    return S_OK;
}

ULONG __stdcall CAnimals::AddRef()
{
    InterlockedIncrement(&m_nCnt);
    return m_nCnt;
}

ULONG __stdcall CAnimals::Release()
{
    if (m_nCnt > 0)
        InterlockedDecrement(&m_nCnt);

    if (m_nCnt == 0)
        delete this;

    return m_nCnt;
}

HRESULT __stdcall CAnimals::Woof(IN const unsigned char* lpString)
{
    if (lpString)
    {
        printf("Dog: ");
        printf((char*)lpString);
    }
    return S_OK;
}

HRESULT __stdcall CAnimals::Meow(IN const unsigned char* lpString)
{
    if (lpString)
    {
        printf("Cat: ");
        printf((char*)lpString);
    }
    return S_OK;
}

HRESULT __stdcall CAnimals::Oink(IN const unsigned char* lpString)
{
    if (lpString)
    {
        printf("Pig: ");
        printf((char*)lpString);
    }
    return S_OK;
}


//////////////////////////////////////////////////////////////////
//  CAnimalsFactory
//////////////////////////////////////////////////////////////////

CAnimalsFactory::CAnimalsFactory()
{
}

CAnimalsFactory::~CAnimalsFactory()
{
}

HRESULT __stdcall CAnimalsFactory::QueryInterface(REFIID riid, void** ppvObject)
{
    if (!ppvObject)
        return E_POINTER;

    *ppvObject = nullptr;

    if (IID_IClassFactory == riid)
    {
        AddRef();
        *ppvObject = dynamic_cast<IClassFactory*>(this);
    }
    else if (IID_IUnknown == riid)
    {
        AddRef();
        *ppvObject = this;
    }
    else
    {
        return E_NOINTERFACE;
    }

    if (*ppvObject == nullptr)
        return S_FALSE;

    return S_OK;
}

ULONG __stdcall CAnimalsFactory::AddRef()
{
    return 1;
}

ULONG __stdcall CAnimalsFactory::Release()
{
    return 0;
}

HRESULT __stdcall CAnimalsFactory::CreateInstance(IN OPTIONAL IUnknown* pUnkOuter, IN REFIID riid, OUT void** ppvObject)
{
    CAnimals* lpAnimals = new CAnimals;
    return lpAnimals->QueryInterface(riid, ppvObject);
}

HRESULT __stdcall CAnimalsFactory::LockServer(IN BOOL fLock)
{
    if (fLock)
    {
        InterlockedIncrement(&m_nLockTick);
    }
    else
    {
        if (m_nLockTick > 0)
            InterlockedDecrement(&m_nLockTick);
    }

    return S_OK;
}

STDAPI DllGetClassObject(const CLSID& rclsid, const IID& riid, void** ppv)
{
    if (rclsid == CLSID_ANIMALS)
    {
        return AnimalsFactory.QueryInterface(riid, ppv);
    }
    else
    {
        return S_FALSE;
    }
}

STDAPI DllCanUnloadNow()
{
    if (AnimalsFactory.GetReferences() == 0)
        return S_OK;
    else
        return S_FALSE;
}

